package com.quectel.cms.controller.entranceguard;


import com.google.common.base.Preconditions;
import com.quectel.business.dms.AccessorUtils;
import com.quectel.business.dms.application.access.accessor.ieg.AbstractIntelligentEntranceGuardAccessor;
import com.quectel.business.dms.application.access.accessor.ieg.model.DeviceFaceSendCmd;
import com.quectel.cms.controller.BaseController;
import com.quectel.cms.util.SessionHolder;
import com.quectel.cms.vo.device.entranceguard.EntranceGuardAuthVo;
import com.quectel.cms.vo.device.entranceguard.EntranceGuardCancelAuthVo;
import com.quectel.constant.core.device.DeviceCategoryCodeConstants;
import com.quectel.constant.core.entranceguard.EntranceGuardPersonConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.log.annotation.Log;
import com.quectel.constant.global.annotation.RequirePermissions;
import com.quectel.core.module.device.dto.DeviceCategoryDto;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.dto.DeviceTypeDto;
import com.quectel.core.module.device.service.DeviceCategoryService;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.core.module.entranceguard.dto.EntranceGuardPersonDto;
import com.quectel.core.module.entranceguard.service.EntranceGuardPersonService;
import com.quectel.core.module.household.dto.HouseholdDto;
import com.quectel.core.module.household.dto.HouseholdRoomDto;
import com.quectel.core.module.household.service.HouseholdRoomService;
import com.quectel.core.module.household.service.HouseholdService;
import com.quectel.core.module.village.service.VillageRoomService;
import com.quectel.util.common.Page;
import com.quectel.util.common.Request;
import com.quectel.util.common.Response;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
 * @author caolu
 * @email louis.cao@quectel.com
 * @date 2021-11-17 13:42:49
 */
@RestController
@RequestMapping("entranceGuardPerson")
@Tag(name = "EntranceGuardPersonController", description = "门禁和人员的绑定关系相关api")
public class EntranceGuardPersonController extends BaseController {

    @DubboReference
    private EntranceGuardPersonService entranceGuardPersonService;
    @DubboReference
    private VillageRoomService villageRoomService;
    @DubboReference
    private HouseholdRoomService householdRoomService;
    @DubboReference
    private DeviceTypeService deviceTypeService;
    @DubboReference
    private HouseholdService householdService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private DeviceCategoryService deviceCategoryService;

    private static final String LOCK_KEY = "household_entrance_auth_lock:";


    /**
     * 列表
     */
    @GetMapping("list")
    @RequirePermissions(values = "entranceGuard:authRecord")
    @Operation(summary = "门禁和人员的绑定关系的分页查询列表")
    public Response<Page<EntranceGuardPersonDto>> list(
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize
    ) {
        //查询列表数据
        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        super.fillAuthorityData(request.getParams());
        int total = entranceGuardPersonService.queryTotal(request.getParams());

        List<EntranceGuardPersonDto> list = null;
        if (total > 0) {
            list = entranceGuardPersonService.queryList(request.getParams());
        } else {
            list = new ArrayList<>();
        }

        Page<EntranceGuardPersonDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, list);
        return Response.<Page<EntranceGuardPersonDto>>ok().wrap(page);
    }

    /**
     * 住户授权
     */
    @PostMapping("auth")
    @RequirePermissions(values = "household:auth")
    @Operation(summary = "住户授权")
    @Log(express = "住户授权")
    public Response<Object> auth(@RequestBody EntranceGuardAuthVo authVo) {
        Long tenantId = Objects.requireNonNull(SessionHolder.getSysUser()).getTenantId();

        final Date currentDate = new Date();
        // 授权代码后续需要完善在 business 包下
        if (authVo.getSendType() != null && authVo.getSendType().equals((byte) 1)) {
            // 人脸类授权
            if (CollectionUtils.isEmpty(authVo.getPersonIds())) {
                return Response.ok();
            }
            List<HouseholdDto> householdDtos = authVo.getPersonIds().parallelStream()
                    .map(id -> householdService.selectCacheById(id)).collect(Collectors.toList());
            Map<String, List<HouseholdDto>> deviceHouseholdMap = new HashMap<>();
            if (authVo.getAuthType().equals((byte) 1)) {
                // 自动授权，找到房屋设备
                DeviceCategoryDto deviceCategoryDto = deviceCategoryService.selectCacheByCode(DeviceCategoryCodeConstants.INTELLIGENT_ENTRANCE_GUARD);
                for (HouseholdDto householdDto : householdDtos) {
                    List<HouseholdRoomDto> householdRoomDtos = householdRoomService.selectByHouseholdId(householdDto.getId());
                    for (HouseholdRoomDto householdRoomDto : householdRoomDtos) {
                        List<DeviceDto> deviceDtos = deviceService.queryAtlasBindDevices(householdRoomDto.getRoomId())
                                .parallelStream().filter(o -> deviceCategoryDto.getId().equals(o.getDeviceCategoryId()))
                                .collect(Collectors.toList());
                        for (DeviceDto deviceDto : deviceDtos) {
                            List<HouseholdDto> ov = deviceHouseholdMap.putIfAbsent(deviceDto.getUniqueId()
                                    , new ArrayList<>(Collections.singletonList(householdDto)));
                            if (CollectionUtils.isNotEmpty(ov)) {
                                ov.add(householdDto);
                            }
                        }
                    }
                }
            } else {
                List<DeviceDto> devices = authVo.getDevices();
                if (CollectionUtils.isNotEmpty(devices)) {
                    for (DeviceDto device : devices) {
                        List<HouseholdDto> ov = deviceHouseholdMap.putIfAbsent(device.getUniqueId(), householdDtos);
                        if (CollectionUtils.isNotEmpty(ov)) {
                            ov.addAll(householdDtos);
                        }
                    }
                }
            }
            String[] keys = authVo.getPersonIds().parallelStream().map(o -> LOCK_KEY + o).toArray(String[]::new);
            Lock multiSpinLock = RedisUtils.getMultiSpinLock(keys);
            multiSpinLock.lock();
            try {
                Map<String, List<DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo>> faceInfoMap = new HashMap<>();

                Map<Long, List<EntranceGuardPersonDto>> personEntranceGuardPersonMap = new HashMap<>();

                // 门禁和人员的关系更新
                for (Map.Entry<String, List<HouseholdDto>> stringListEntry : deviceHouseholdMap.entrySet()) {
                    for (HouseholdDto householdDto : stringListEntry.getValue()) {
                        List<EntranceGuardPersonDto> entranceGuardPersonDtos = personEntranceGuardPersonMap.computeIfAbsent(householdDto.getId()
                                , id -> entranceGuardPersonService.queryAuthByPersonId(SystemConstants.HOUSEHOLD, householdDto.getId()));
                        EntranceGuardPersonDto entranceGuardPersonDto = entranceGuardPersonDtos.parallelStream()
                                .filter(o -> o.getDevice() != null && o.getDevice().getUniqueId().equals(stringListEntry.getKey()))
                                .findFirst().orElse(null);
                        if (entranceGuardPersonDto == null) {
                            // 还不存在该人员和设备的关系
                            EntranceGuardPersonDto saveObj = new EntranceGuardPersonDto();

                            saveObj.setTenantId(tenantId);
                            saveObj.setAuthType(EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType());

                            DeviceDto deviceDto = deviceService.selectCacheByUniqueId(stringListEntry.getKey(), tenantId);
                            saveObj.setDeviceId(deviceDto.getId());
                            saveObj.setPersonType(EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType());
                            saveObj.setPersonId(householdDto.getId());
                            saveObj.setAuthStatus(EntranceGuardPersonConstants.AuthStatusEnum.NOT_RESPONDING.getAuthStatus());
                            saveObj.setCreateTime(currentDate);
                            entranceGuardPersonService.save(saveObj);
                        } else {
                            if (EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType().equals(entranceGuardPersonDto.getAuthType())
                                    && EntranceGuardPersonConstants.AuthStatusEnum.SEND_SUCCESS.getAuthStatus().equals(entranceGuardPersonDto.getAuthStatus())) {
                                // 授权成功的就不再授权了
                                continue;
                            }
                            EntranceGuardPersonDto updateObj = new EntranceGuardPersonDto();
                            updateObj.setId(entranceGuardPersonDto.getId());
                            updateObj.setAuthType(EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType());
                            updateObj.setAuthStatus(EntranceGuardPersonConstants.AuthStatusEnum.NOT_RESPONDING.getAuthStatus());
                            entranceGuardPersonService.updateById(updateObj);
                        }

                        // 组装下发的参数
                        DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo personFaceInfo = new DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo();
                        personFaceInfo.setPersonId(householdDto.getId());
                        personFaceInfo.setPersonType((byte)1);
                        personFaceInfo.setName(householdDto.getName());
                        personFaceInfo.setSex(householdDto.getGender());
                        personFaceInfo.setFaceImg(householdDto.getFaceImg());
                        personFaceInfo.setCardNumber(householdDto.getCardNo());
                        personFaceInfo.setMobile(householdDto.getMobile());
                        List<DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo> personFaceInfos = faceInfoMap.putIfAbsent(stringListEntry.getKey()
                                , new ArrayList<>(Collections.singletonList(personFaceInfo)));
                        if (CollectionUtils.isNotEmpty(personFaceInfos)) {
                            personFaceInfos.add(personFaceInfo);
                        }
                    }
                }

                // 不同设备类型划分开
                Map<Long, List<DeviceFaceSendCmd.DeviceInfo>> typeDeviceInfoMap = new HashMap<>();
                for (Map.Entry<String, List<DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo>> stringListEntry : faceInfoMap.entrySet()) {
                    DeviceDto deviceDto = deviceService.selectCacheByUniqueId(stringListEntry.getKey(), tenantId);

                    DeviceFaceSendCmd.DeviceInfo deviceInfo = new DeviceFaceSendCmd.DeviceInfo();
                    deviceInfo.setTenantId(tenantId);
                    deviceInfo.setDeviceUniqueId(stringListEntry.getKey());
                    deviceInfo.setPersonFaceInfoList(stringListEntry.getValue());

                    List<DeviceFaceSendCmd.DeviceInfo> deviceInfoList = typeDeviceInfoMap.putIfAbsent(deviceDto.getDeviceTypeId(), new ArrayList<>(Collections.singletonList(deviceInfo)));
                    if (CollectionUtils.isNotEmpty(deviceInfoList)) {
                        deviceInfoList.add(deviceInfo);
                    }
                }

                // 遍历开始下发
                for (Map.Entry<Long, List<DeviceFaceSendCmd.DeviceInfo>> longListEntry : typeDeviceInfoMap.entrySet()) {
                    DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(longListEntry.getKey());
                    AbstractIntelligentEntranceGuardAccessor intelligentEntranceGuardAccessor = AccessorUtils.getIntelligentEntranceGuardAccessor(deviceTypeDto);
                    Preconditions.checkNotNull(intelligentEntranceGuardAccessor);

                    DeviceFaceSendCmd.DeviceFaceSendCmdBuilder builder = DeviceFaceSendCmd.builder();
                    builder.type(DeviceFaceSendCmd.TypeEnum.ADD.getType());
                    builder.deviceInfoList(longListEntry.getValue());
                    intelligentEntranceGuardAccessor.authPerson(builder.build());
                }

            }finally {
                multiSpinLock.unlock();
            }

        }
        return Response.ok();
    }

    @PostMapping("cancelAuth")
    @RequirePermissions(values = "household:auth")
    @Operation(summary = "取消授权")
    @Log(express = "取消授权")
    public Response<Object> cancelAuth(@RequestBody @Validated EntranceGuardCancelAuthVo cancelAuthVo) {
        HouseholdDto householdDto = householdService.selectCacheById(cancelAuthVo.getPersonId());
        if (Objects.isNull(householdDto)) {
            return Response.error("住户不存在");
        }
        Lock spinLock = RedisUtils.getSpinLock(LOCK_KEY + cancelAuthVo.getPersonId());
        spinLock.lock();
        try {
            DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo personFaceInfo = new DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo();
            personFaceInfo.setPersonId(householdDto.getId());
            personFaceInfo.setPersonType((byte)1);
            personFaceInfo.setName(householdDto.getName());
            personFaceInfo.setSex(householdDto.getGender());
            personFaceInfo.setFaceImg(householdDto.getFaceImg());
            personFaceInfo.setCardNumber(householdDto.getCardNo());
            personFaceInfo.setMobile(householdDto.getMobile());

            List<EntranceGuardPersonDto> entranceGuardPersonDtos = entranceGuardPersonService.queryAuthByPersonId(SystemConstants.HOUSEHOLD, cancelAuthVo.getPersonId());

            Map<Long, List<DeviceFaceSendCmd.DeviceInfo>> typeDeviceInfoMap = new HashMap<>();
            entranceGuardPersonDtos.parallelStream().filter(o -> (EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType().equals(o.getAuthType())
                            && EntranceGuardPersonConstants.AuthStatusEnum.SEND_SUCCESS.getAuthStatus().equals(o.getAuthStatus()))
                            || (EntranceGuardPersonConstants.AuthTypeEnum.DEL.getAuthType().equals(o.getAuthType())
                            && EntranceGuardPersonConstants.AuthStatusEnum.SEND_ERROR.getAuthStatus().equals(o.getAuthStatus())))
                    .filter(o -> Objects.nonNull(o.getDevice())).forEach(o->{

                        // 更新关系
                        EntranceGuardPersonDto updateObj = new EntranceGuardPersonDto();
                        updateObj.setId(o.getId());
                        updateObj.setAuthType(EntranceGuardPersonConstants.AuthTypeEnum.DEL.getAuthType());
                        updateObj.setAuthStatus(EntranceGuardPersonConstants.AuthStatusEnum.NOT_RESPONDING.getAuthStatus());
                        entranceGuardPersonService.updateById(updateObj);

                        DeviceFaceSendCmd.DeviceInfo deviceInfo = new DeviceFaceSendCmd.DeviceInfo();
                        deviceInfo.setTenantId(o.getTenantId());
                        deviceInfo.setDeviceUniqueId(o.getDevice().getUniqueId());
                        deviceInfo.setPersonFaceInfoList(Collections.singletonList(personFaceInfo));

                        List<DeviceFaceSendCmd.DeviceInfo> deviceInfoList = typeDeviceInfoMap.putIfAbsent(o.getDevice().getDeviceTypeId(), new ArrayList<>(Collections.singletonList(deviceInfo)));
                        if (CollectionUtils.isNotEmpty(deviceInfoList)) {
                            deviceInfoList.add(deviceInfo);
                        }
                    });

            // 遍历开始下发
            for (Map.Entry<Long, List<DeviceFaceSendCmd.DeviceInfo>> longListEntry : typeDeviceInfoMap.entrySet()) {
                DeviceTypeDto deviceTypeDto = deviceTypeService.selectCacheById(longListEntry.getKey());
                AbstractIntelligentEntranceGuardAccessor intelligentEntranceGuardAccessor = AccessorUtils.getIntelligentEntranceGuardAccessor(deviceTypeDto);
                Preconditions.checkNotNull(intelligentEntranceGuardAccessor);

                DeviceFaceSendCmd.DeviceFaceSendCmdBuilder builder = DeviceFaceSendCmd.builder();
                builder.type(DeviceFaceSendCmd.TypeEnum.DEL.getType());
                builder.deviceInfoList(longListEntry.getValue());
                intelligentEntranceGuardAccessor.authPerson(builder.build());
            }
        }finally {
            spinLock.unlock();
        }
        return Response.ok();
    }

}
